#include "base32.h"
/* vim:ts=4:sw=4:noet
 * (tabspace=4)2
 * 
 * Copyright (C) 2004, 2005 Walter Doekes, <walter@djcvt.net>.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "endian.h"
#include "texts.h"
#ifdef BIG_ENDIAN
#	include <malloc.h>
#	include <string.h>
#endif /* BIG_ENDIAN */
#include <assert.h>

static const int8_t base32_table[] = {
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,26,27,28,29,30,31,-1,-1,-1,-1,-1,-1,-1,-1,
	-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
	15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
	-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
	15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
	-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
};

static const unsigned char base32_alpha[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";

int uint64tobase32(unsigned char* dest, const uint64_t* src, unsigned len) {
#ifdef BIG_ENDIAN
	int i;
	uint8_t* tmp = (uint8_t*)malloc(len * 8);
	if(tmp == NULL) {
#ifdef USE_SETERROR2
		set_error("malloc", -1);
#endif /* USE_SETERROR2 */
		return -1;
	}
	for(i = 0; i < len * 8; ++i)
		tmp[i ^ 7] = ((uint8_t*)src)[i];
	i = uint8tobase32(dest, tmp, len * 8);
	free(tmp);
	return i;
#else /* !BIG_ENDIAN */
	return uint8tobase32(dest, (const uint8_t*)src, len * 8);
#endif /* !BIG_ENDIAN */
}

int uint8tobase32(unsigned char* dest, const uint8_t* src, unsigned len) {
	unsigned index = 0;
	uint8_t word;
	const uint8_t* srce = src + len;
	while(src != srce) {
		/* Spanning byte boundary? */
		if(index > 3) {
			word = (*src & (0xFF >> index));
			index = (index + 5) % 8;
			word <<= index;
			if(src + 1 != srce)
				word |= *(src + 1) >> (8 - index);
			++src;
		} else {
			word = (*src >> (8 - (index + 5))) & 0x1F;
			index = (index + 5) % 8;
			if(index == 0)
				++src;
		}
		assert(word < 32);
		*dest++ = base32_alpha[word];
	}
	*dest = '\0';
	return 0;
}

int base32touint64(uint64_t* dest, const unsigned char* src, unsigned len) {
#ifdef BIG_ENDIAN
	int i;
	uint8_t* tmp = (uint8_t*)malloc(len * 8);
	if(tmp == NULL) {
#ifdef USE_SETERROR2
		set_error("malloc", -1);
#endif /* USE_SETERROR2 */
		return -1;
	}
	memset(tmp, 0, len * 8);
	i = base32touint8(tmp, src, len * 8);
	for(i = 0; i < len * 8; ++i)
		((uint8_t*)dest)[i ^ 7] = tmp[i];
	free(tmp);
	return i;
#else /* !BIG_ENDIAN */
	return base32touint8((uint8_t*)dest, src, len * 8);
#endif /* !BIG_ENDIAN */
}

int base32touint8(uint8_t* dest, const unsigned char* src, unsigned len) {
	unsigned index = 0;
	int8_t tmp;
	const uint8_t* deste = dest + len;
	for(; dest != deste; ++src) {
		if((tmp = base32_table[*src]) == -1) {
#ifdef USE_SETERROR2
			set_error("base32touint8", BASE32_INVALID_CHARACTER);
#endif /* USE_SETERROR2 */
			return -1;
		}
		if(index <= 3) {
			index = (index + 5) % 8;
			if(index == 0)
				*dest++ |= tmp;
			else
				*dest |= tmp << (8 - index);
		} else {
			index = (index + 5) % 8;
			*dest++ |= (tmp >> index);
			*dest |= tmp << (8 - index);
		}
	}
	return 0;
}

#ifdef TEST_BASE32_C

#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char** argv) {
	int ret = 0;
	if(argc == 3) {
		int len = atoi(argv[2]);
		char* out = (char*)malloc((len + 1) * 3 * sizeof(char));
		ret = uint8tobase32(out, argv[1], len);
		printf("uint8tobase32 = %i: %s\r\n", ret, out);
		free(out);
	} else if(argc == 2) {
		int len = strlen(argv[1]);
		char* out = (char*)malloc((len + 1) * sizeof(char));
		memset(out, 0, (len + 1) * sizeof(char));
		ret = base32touint8(out, argv[1], len);
		printf("%s", out);
		free(out);
	} else if(argc == 1) {
		uint64_t x = 0x8B630E030AD09E5Dull;
		uint64_t y = 0x0ull;
		char buf[15];
		printf("%llx\r\n", x);
		uint64tobase32(buf, &x, 1);
		printf("%s\r\n", buf);
		base32touint64(&y, buf, 1);
		printf("%llx\r\n", y);
	}	
	return ret;
}

#endif /* TEST_BASE32_C */
